Obsidian のデイリーノートを一括置換するスクリプト

作成日
更新日

Obsidianデイリーノートを一括置換するスクリプト

  • Obsidian のディレクトリ構造を一括で変換するスクリプトと同様に行う。
import os
import shutil
import re
import uuid

# ルートディレクトリ
PUBLIC_DIR = os.path.join(os.getcwd(), "public")

# 出力先フォルダ
DAILY_NOTE_DIR = os.path.join(os.getcwd(), "100_DailyNote")
IMAGES_DIR = os.path.join(os.getcwd(), "002_Images")

# 必要なフォルダを作成
os.makedirs(DAILY_NOTE_DIR, exist_ok=True)
os.makedirs(IMAGES_DIR, exist_ok=True)

# `coverImage` のパスを取得し、見つからなければ適切に補正する
def get_cover_image_path(images_folder, cover_image):
    if not cover_image or cover_image.strip() == "":
        return None

    cover_image_path = os.path.join(images_folder, cover_image)

    # `coverImage` に拡張子がある場合は、そのままチェック
    if os.path.exists(cover_image_path):
        return cover_image_path

    # 拡張子がない場合、`.png`, `.jpg`, `.jpeg` を試す
    if "." not in cover_image:  
        for ext in [".png", ".jpg", ".jpeg"]:
            temp_path = os.path.join(images_folder, cover_image + ext)
            if os.path.exists(temp_path):
                return temp_path

    print(f"カバー画像が見つかりません: {cover_image}")
    return None

# `coverImage` を移動し、メタデータを更新し、挿入用の Markdown を生成
def process_cover_image(cover_image, note_date, year):
    images_folder = os.path.join(PUBLIC_DIR, str(year), "images")
    cover_image_path = get_cover_image_path(images_folder, cover_image)

    if cover_image_path is None:
        return None, ""

    # **ファイルごとに新しいUUIDを生成**
    unique_id = uuid.uuid4().hex
    ext = os.path.splitext(cover_image_path)[1]  # `.png`, `.jpg`, `.jpeg` など
    new_cover_image_name = f"{note_date}-{unique_id}{ext}"
    dst_image_path = os.path.join(IMAGES_DIR, new_cover_image_name)

    try:
        shutil.move(cover_image_path, dst_image_path)
        markdown_link = f"[![](../002_Images/{new_cover_image_name})](../002_Images/{new_cover_image_name})\n\n"
        return new_cover_image_name, markdown_link
    except Exception as e:
        print(f"カバー画像移動エラー: {cover_image_path} → {dst_image_path} ({e})")
        return None, ""

# **該当ノート内の画像のみを移動**
def move_referenced_images(source_folder, note_date, content):
    image_map = {}
    images_path = os.path.join(source_folder, "images")

    if os.path.exists(images_path):
        # **マークダウン内で参照されている画像のみを取得**
        referenced_images = re.findall(r'!\[[^\]]*\]\(images/([^)\s]+)\)', content)

        for image in referenced_images:
            src_image_path = os.path.join(images_path, image)
            if os.path.exists(src_image_path):
                name, ext = os.path.splitext(image)
                new_image_name = f"{note_date}-{uuid.uuid4().hex}{ext}"
                dst_image_path = os.path.join(IMAGES_DIR, new_image_name)

                try:
                    shutil.move(src_image_path, dst_image_path)
                    image_map[image] = new_image_name
                except Exception as e:
                    print(f"画像移動エラー: {src_image_path} → {dst_image_path} ({e})")

    # **画像フォルダが空になったら削除**
    if os.path.exists(images_path) and not os.listdir(images_path):
        shutil.rmtree(images_path)

    return image_map

# デイリーノートを処理
def process_daily_notes(year):
    notes_dir = os.path.join(PUBLIC_DIR, str(year))

    if not os.path.exists(notes_dir):
        return

    for file in sorted(os.listdir(notes_dir)):  # ファイルごとに正しく処理
        file_path = os.path.join(notes_dir, file)
        if file.endswith(".md"):
            # **各ファイルの `note_date` を個別に取得**
            note_date = os.path.splitext(file)[0]  # `YYYY-MM-DD`
            new_md_path = os.path.join(DAILY_NOTE_DIR, f"{note_date}.md")

            with open(file_path, "r", encoding="utf-8") as f:
                content = f.read()

            # `coverImage` の値を取得(ない場合は None にする)
            cover_image_match = re.search(r'coverImage:\s*(\S+)', content)
            cover_image = cover_image_match.group(1) if cover_image_match else None

            # `coverImage` を処理(UUID付き新ファイル名へ変更)
            cover_image_markdown = ""
            if cover_image:
                new_cover_image_name, cover_image_markdown = process_cover_image(cover_image, note_date, year)
                if new_cover_image_name:
                    content = re.sub(r'coverImage:\s*\S+', f'coverImage: {new_cover_image_name}', content)

            # **`coverImage` の画像をメタデータの直後に挿入**
            if cover_image_markdown:
                content = re.sub(r'^(---\n.*?---\n)', rf'\1\n{cover_image_markdown}', content, count=1, flags=re.DOTALL)

            # **該当ノートで参照されている画像のみを移動**
            image_map = move_referenced_images(notes_dir, note_date, content)
            for old_name, new_name in image_map.items():
                escaped_old_name = re.escape(old_name)
                content = re.sub(
                    rf'!\[[^\]]*\]\(images/{escaped_old_name}\)',
                    rf'![](../002_Images/{new_name})',
                    content
                )
                content = re.sub(
                    rf'\[!\[[^\]]*\]\(images/{escaped_old_name}\)\]\(images/[^)]*\)',
                    rf'[![](../002_Images/{new_name})](../002_Images/{new_name})',
                    content
                )

            # **変更を `100_DailyNote` に保存**
            with open(new_md_path, "w", encoding="utf-8") as f:
                f.write(content)

            # **元の `.md` を削除**
            os.remove(file_path)

# **すべての年を処理**
for year in range(2024, 2030):  # 必要に応じて範囲を調整
    process_daily_notes(year)

print("デイリーノートの変換が完了しました。")
サイトアイコン
公開日
更新日